/* $RCSFile: Rewrite.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.Rewrite.NET/Rewrite.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:30  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Globalization;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.Web.Configuration;
using System.Web.Caching;
using System.Reflection;
using System.Diagnostics;

namespace Rewrite.NET 
{
    public class Rewrite : IHttpModule 
	{
        string[] AvoidEndUrlList            =   null;
        string[] AvoidAnyUrlList            =   null;
        const string strAvoidEndUrlList     =   "AvoidEndUrlList";
        const string strAvoidAnyUrlList     =   "AvoidAnyUrlList";

        /// <summary>
		/// Init is required from the IHttpModule interface
		/// </summary>
		/// <param name="Appl"></param>
		public void Init(HttpApplication Appl) 
		{
	        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]Rewrite Init BEG.",RewriteTraceSwitch.Sw.Info);
			Appl.BeginRequest	+=	new System.EventHandler(Rewrite_BeginRequest);
            if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]Rewrite Init END.",RewriteTraceSwitch.Sw.Info);
		}

		public void ReadConfig() 
		{
	        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]Rewrite ReadConfig BEG.",RewriteTraceSwitch.Sw.Info);
            HttpContext Context                 =   HttpContext.Current;
            if (Context.Cache[strAvoidEndUrlList] != null &&
                Context.Cache[strAvoidAnyUrlList] != null)
            {
                AvoidEndUrlList = (string[])Context.Cache[strAvoidEndUrlList];
                AvoidAnyUrlList = (string[])Context.Cache[strAvoidAnyUrlList];
	            if ( RewriteTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine("[V]Rewrite ReadConfig END found in cache.",RewriteTraceSwitch.Sw.Info);
                return;
            }

            EngineConfig conf       =   EngineConfig.GetConfig();
            if (conf.Rules.Count == 0)
            {
                if (RewriteTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine("[V]Rewrite ReadConfig END no rules found.", RewriteTraceSwitch.Sw.Info);
                return;
            }

            string aUrls                        =   conf.AvoidEndUrlList;
            if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite ReadConfig AvoidEndUrlList: '{0}'",aUrls),RewriteTraceSwitch.Sw.Info);
            AvoidEndUrlList                     =   aUrls.Split(new char[]{';'},StringSplitOptions.RemoveEmptyEntries);
            // trim all strings
            for ( int i=0 ; i < AvoidEndUrlList.Length ; i++ )
                AvoidEndUrlList[i]  =   AvoidEndUrlList[i].Trim();
            aUrls                               =   conf.AvoidAnyUrlList;
            if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite ReadConfig AvoidAnyUrlList: '{0}'",aUrls),RewriteTraceSwitch.Sw.Info);
            AvoidAnyUrlList                     =   aUrls.Split(new char[]{';'},StringSplitOptions.RemoveEmptyEntries);
            // trim all strings
            for ( int i=0 ; i < AvoidAnyUrlList.Length ; i++ )
                AvoidAnyUrlList[i]  =   AvoidAnyUrlList[i].Trim();
            if (RewriteTraceSwitch.Sw.TraceVerbose && AvoidAnyUrlList != null)
            {
                Trace.WriteLine(string.Format(CultureInfo.InvariantCulture,"[V]Rewrite ReadConfig AvoidAnyUrlList.Length: {0}", AvoidAnyUrlList.Length), RewriteTraceSwitch.Sw.Info);
                if ( AvoidAnyUrlList.Length > 0 )
                    Trace.WriteLine(string.Format(CultureInfo.InvariantCulture,"[V]Rewrite ReadConfig AvoidAnyUrlList[0]: [{0}]", AvoidAnyUrlList[0]), RewriteTraceSwitch.Sw.Info);
            }

            Context.Cache.Add(strAvoidEndUrlList,AvoidEndUrlList,null,Cache.NoAbsoluteExpiration,new TimeSpan(conf.CacheTimeoutHours,conf.CacheTimeoutMinutes,0),CacheItemPriority.Normal,null);
            Context.Cache.Add(strAvoidAnyUrlList,AvoidAnyUrlList,null,Cache.NoAbsoluteExpiration,new TimeSpan(conf.CacheTimeoutHours,conf.CacheTimeoutMinutes,0),CacheItemPriority.Normal,null);

            if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]Rewrite ReadConfig END.",RewriteTraceSwitch.Sw.Info);
		}

		/// <summary>
		/// Dispose is required from the IHttpModule interface
		/// </summary>
		public void Dispose() 
        {
			//make sure you clean up after yourself
		}

        public bool IsAjaxRequest(HttpApplication Appl) 
        {
            if ( Appl.Request.Headers["x-microsoftajax"] != null)
                return true;
            return false;
        }

        /// <summary>
		/// To handle the starting of the incoming request
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		public void Rewrite_BeginRequest(object sender,EventArgs args) 
		{
			try
			{
				//cast the sender to a HttpApplication object
				HttpApplication Appl	=	(HttpApplication)sender;

                ReadConfig();

				//CS 18/5/7 avoid unecessary work, check the url for predefined values 
				string _rawUrl	        =	Engine.Getpath(Appl).ToLower(CultureInfo.InvariantCulture);
                int idx                 =   _rawUrl.IndexOf('?');
                string avoidUrl         =   (idx > 0) ? _rawUrl.Substring(0,idx):_rawUrl;
		        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite Rewrite_BeginRequest BEG rawUrl: {0}", _rawUrl),RewriteTraceSwitch.Sw.Info);
                bool check              =   true;
                for ( int i=0 ; check && ( i < AvoidEndUrlList.Length) ; i++ )
                {
			        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite Rewrite_BeginRequest AvoidEndUrlList[{0}]: {1}", i,AvoidEndUrlList[i]),RewriteTraceSwitch.Sw.Info);
                    if ( AvoidEndUrlList[i] == string.Empty )
                        continue;
                    if ( avoidUrl.EndsWith(AvoidEndUrlList[i],StringComparison.InvariantCultureIgnoreCase) )
                        check           =   false;
                }
                for ( int i=0 ; check && ( i < AvoidAnyUrlList.Length ); i++ )
                {
			        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite Rewrite_BeginRequest AvoidAnyUrlList[{0}]: {1}", i,AvoidAnyUrlList[i]),RewriteTraceSwitch.Sw.Info);
                    if ( AvoidAnyUrlList[i] == string.Empty )
                        continue;
                    if ( avoidUrl.IndexOf(AvoidAnyUrlList[i],StringComparison.InvariantCultureIgnoreCase)  > 0 )
                        check           =   false;
                }
                if ( !check )
                {
			        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite Rewrite_BeginRequest no rewrite for: {0}", _rawUrl),RewriteTraceSwitch.Sw.Info);
                    return;
                }

				string r				=	string.Empty;
				bool changed            =   DecodeUrl(Appl,ref r);
	
		        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite Rewrite_BeginRequest after engine.execute changed: {0}, result: '{1}'",changed, r),RewriteTraceSwitch.Sw.Info);
				//only redirect if we have too
				if( changed ) 
					SendToNewUrl(r, Appl);
			}
			catch (Exception ex)
			{
				Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]Rewrite Rewrite_BeginRequest ex : {0}",ex));
			}
		}

        public bool DecodeUrl(HttpApplication Appl,ref string r)
        {
	        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite DecodeUrl RawUrl: {0}, original url: '{1}'",Appl.Request.RawUrl, r),RewriteTraceSwitch.Sw.Info);
			Engine e	            =	new Engine(Appl);
            bool ret                =   e.Execute(ref r);		
	        if ( RewriteTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]Rewrite DecodeUrl Changed: {0}, url: '{1}'",ret, r),RewriteTraceSwitch.Sw.Info);
            return ret;
        }
        
        
        
        /* interesting but slow especially on IIS7
        void ChangeServerVariable(HttpApplication application,string NewVar,string val)
        {

            BindingFlags temp = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

            MethodInfo addStatic = null;
            MethodInfo makeReadOnly = null;
            MethodInfo makeReadWrite = null;


            Type type = application.Request.ServerVariables.GetType();
            MethodInfo[] methods = type.GetMethods(temp);
            foreach (MethodInfo method in methods)
            {
                switch (method.Name)
                {
                  case "MakeReadWrite": makeReadWrite = method;
                    break;
                  case "MakeReadOnly": makeReadOnly = method;
                    break;
                  case "AddStatic": addStatic = method;
                    break;
                }
            }

            makeReadWrite.Invoke(application.Request.ServerVariables, null);
            string[] values = { NewVar, val };
            addStatic.Invoke(application.Request.ServerVariables, values);
            makeReadOnly.Invoke(application.Request.ServerVariables, null);
        }
        */

		public void SendToNewUrl(string url,System.Web.HttpApplication Appl) 
		{
            // CS 13/2/7 Beware, always use false here to avoid breaking relative path used for .NET 2 themes
			Appl.Context.RewritePath(url,false);
            Appl.Context.Items["HTTP_XW_ORIGINAL_URL"]  = Engine.Getpath(Appl);
            // slow    ChangeServerVariable(Appl,"HTTP_XW_ORIGINAL_URL",Engine.Getpath(Appl));
		}
	}
}
